import os
from datetime import datetime


from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.urls import reverse

# Create your views here.
# django会把用户的请求信息封装成对象，作为参数传递进来，也就是request（自定义参数名见名知意），注意不要写错成requests了加s是请求模块的名称
# def text(request,info):
# def text(request,*args,**kwargs):  # *args可以不用写，写了是为了防止报错
from django.utils.decorators import method_decorator
from django.views import View

from demo.form import ArticleForms
from demo.models import Classes, UserDetail, Article, Label
from my_django_study.settings import MEDIA_ROOT


def text(request, **kwargs):
    # 元组不定长*args  字典不定长**kwargs
    # print(info) # 保存了地址里面的参数字符
    # print(args) # 保存了地址里面的参数字符
    print(kwargs) # 保存了地址里面的参数字符
    # 用get方法获取值而不用[]方法获取，是因为get如果取不到字典的值，也不会报错
    info1 = kwargs.get('info')
    print(info1)
    print('text')
    # 返回一个响应对象 返回给用户的数据
    # 也可以返回一个html文件，可以写行内样式但是不能写文件内的样式和文件外的样式
    # return HttpResponse("你好django")
    return HttpResponse("<h1 style='color:red;'>你好 django<h1/>")

def Tv(request, **kwargs):
    return HttpResponse("<h1> 电视剧 <h1/>")







# 自定义字典模拟数据，之后可以连数据库
# 定义字典来模拟数据
# data = {
#     'python': [{
#         # 标题
#         'title': '新手Python环境配置以及pip安装教程',
#         # 描述信息
#         'desc': 'pip 是Python包管理工具，提供了对 Python 包的查找、下载、安装、卸载的功能，目前Python 3.4 和 2.7 及以上版本都有配套安装，一般pip的位置在...\python\Scripts\文件夹里面，而在其他版本需要自行下载。',
#         # 详情地址
#         'detail': 'https://blog.csdn.net/weixin_66337429/article/details/127759593?spm=1001.2100.3001.7377&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-2-127759593-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-2-127759593-null-null.nonecase',
#         # 图片地址
#         'img': 'https://img-blog.csdnimg.cn/e3d7931a476f4c7aa7b90085d99630a5.png?x-oss-process=image/resize,m_fixed,h_300,image/format,png',
#         'create_time':datetime(2025,7,14,9,3),
#     },
#         {
#             "title": "【Python】【数据分析】Python 数据分析与可视化：全面指南",
#             "desc": "本文演示了如何使用 Python 进行数据分析与可视化。关键步骤包括数据处理、分析和可视化。使用pandas进行数据清洗与分析，使用matplotlib和seaborn进行可视化，展示了不同的图表和分析结果。在实际应用中，数据分析与可视化是一个迭代的过程，您可以根据需求不断调整和优化。希望这篇博客能为您提供一个清晰的入门指南，助力您在数据分析与可视化的旅程中不断前行！如果有任何问题或建议，欢迎在评论区讨论！",
#             "detail": "https://blog.csdn.net/Stromboli/article/details/142552381?spm=1000.2115.3001.10526&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase",
#             "img": "https://i-blog.csdnimg.cn/direct/e57771da0f12498cb733a68e6dfff5ef.png",
#             'create_time': datetime(2025, 5, 14, 9, 3),
#         },
#         {
#             "title": "Python窗体编程技术详解",
#             "desc": "本文演示了如何使用 Python 进行数据分析与可视化。关键步骤包括数据处理、分析和可视化。使用pandas进行数据清洗与分析，使用matplotlib和seaborn进行可视化，展示了不同的图表和分析结果。在实际应用中，数据分析与可视化是一个迭代的过程，您可以根据需求不断调整和优化。希望这篇博客能为您提供一个清晰的入门指南，助力您在数据分析与可视化的旅程中不断前行！如果有任何问题或建议，欢迎在评论区讨论！",
#             "detail": "https://blog.csdn.net/Stromboli/article/details/142552381?spm=1000.2115.3001.10526&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase",
#             "img": "https://i-blog.csdnimg.cn/direct/693e9501a2ca4875ae4a1ef7b48b3380.png",
#             'create_time': datetime(2025, 8, 14, 9, 3),
#         },
#     ],
#     "java": [
#         {
#             "title": "【Java】多线程和高并发编程（三）：锁（下）深入ReentrantReadWriteLock",
#             "desc": "写锁加锁流程// 写锁加锁的入口// 阿巴阿巴！！if (!// 读写锁的写锁实现tryAcquire// 拿到当前线程// 拿到state的值// 得到state低16位的值// 判断是否有线程持有着锁资源if (c!= 0) {// 当前没有线程持有写锁，读写互斥，告辞。// 有线程持有写锁，持有写锁的线程不是当前线程，不是锁重入，告辞。// 当前线程持有写锁。锁重入。// 没有超过锁重入的次数，正常 + 1// 尝试获取锁资源// CAS拿锁!",
#             "detail": "https://blog.csdn.net/Stromboli/article/details/142552381?spm=1000.2115.3001.10526&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase",
#             "img": "https://i-blog.csdnimg.cn/direct/693e9501a2ca4875ae4a1ef7b48b3380.png",
#             'create_time': datetime(2025, 4, 14, 9, 3),
#         }
#     ],
#     "deepseek": []
# }


# # 用户已经登录了
# user = {
#     "name":"rose",
#     "sex":"女",
# }
#
#
# # 用户的值为None，保存用户未登录的状态
# user = None


def nav(request, **kwargs):
    # 得到地址栏传递过来的分类
    classes = kwargs.get('classes')
    print(classes)

    # username = request.session.get('username')
    username = request.user.username

    # 得到字典中的文章数据
    # res = data.get(classes)
    # 值为空 flase   所以取反为真
    # 如果分类没有数据就会跳转到not_find页面
    # if not res:
    #     # return redirect('/demo/404')
    #     # reverse可以解析保存在not_find里面的路由是什么样子的
    #     print(reverse('not_find'))
    #     return redirect('not_find')
    # 想要分类下没有文章时显示的是暂无文章，而不是调整not_find页面
    # data.key()获取字典data的所有键的名字
    # if classes not in list(data.keys()):
    #     return redirect('not_find')


    # 得到模型类里面的分类对象
    try:
        # 如果获取不到该分类对象 就会报错 就代表数据库里面没有这个分类
        res = Classes.objects.get(name=classes)
    except Classes.DoesNotExist:
        # 如果报错就跳转的404页面
        return redirect('not_find')



    # return HttpResponse(f'{classes}下的所有{res}')
    # 渲染模板页面
    # return HttpResponse(render(request,'demo/nav.html'))
    return HttpResponse(render(request, 'demo/nav.html',
                               # 传入的内容
                               context={
                                   # "articles": res,
                                   # 通过管理器获得该分类下的所有文章数据
                                   "articles": res.article_set.all(),
                                   # 当前路由访问的分类名 一个
                                   "now_cls":classes,
                                   # 数据里面所有的分类名 全部
                                   # "classes":data.keys(),
                                   # 得到模型类的所有分类对象
                                   "classes":Classes.objects.all(),
                                   "user":username,
                                   # "str1": "hello django",
                                   # "num": 100,
                                   # "html":'<h1>hello django</h1>',
                               }))


def not_find(request, **kwargs):
    return HttpResponse('该页面不存在')

def index(request, **kwargs):
    # django会白动获取请求中的cookie，然片到数据库中的sessionm
    # username = request.session.get('username')
    username = request.user.username
    # return HttpResponse('首页')
    return HttpResponse(render(request,'demo/index.html',context={
        # "classes":data.keys(),
        # 得到模型类的所有分类对象
        "classes":Classes.objects.all(),
        "user":username,
        # "user":User.objects.get(username="rose"),
    }))



def classes(request,**kwargs):
    # # 实例化一个对象 并且传入字段值
    # classes1=Classes(name="python")
    # classes1.save() # 保存
    #
    # # 第二种 创建一个对象
    # classes2=Classes()
    # # # 给模型对象属性赋值
    # classes2.name="java"
    # classes2.save()
    #
    # # 第三种 通过类调用创建的方法创建对象传值
    # Classes.objects.create(name="deepseek")
    #
    # # 第四种 get_or_create获取或者创建
    # # 有数据就获取 没有则添加
    # Classes.objects.get_or_create(name="html")
    #
    # # 查询 所有数据
    # res=Classes.objects.all()
    # for i in res:
    #     print(i.name)
    # # print(res)
    # # # 查询 单条数据 条件查询
    # res1=Classes.objects.get(name="java")
    # print(res1)
    # # # 查询 多条数据 条件查询
    # res2=Classes.objects.filter(name="python")
    # print(res2)
    #
    # # 修改数据
    # # 修改单条数据
    # # 先获取你要修改的数据
    # res=Classes.objects.get(name="html")
    # res.name="HTML"
    # res.save()
    # # 修改多条数据 查询集不是一个对象
    # Classes.objects.filter(name="python").update(name="python3")
    #
    # # 删除单条 和删除多条 delete
    # Classes.objects.get(name="HTML").delete()
    # Classes.objects.filter(name="python3").delete()
    # Classes.objects.filter(name="java")
    #
    # # 查询方法
    # # 获取第一条数据
    # print(Classes.objects.first())
    # print('-'*20)
    # # 获取最后一条数据
    # print(Classes.objects.last())
    # print('-' * 20)
    # # 排除查询
    # print(Classes.objects.exclude(name="python"))
    # print('-' * 20)
    # # 对结果进行排序 只能根据数字来进行排序
    # print(Classes.objects.order_by('id'))
    # print('-' * 20)
    # # 对结果进行逆向排序
    # print(Classes.objects.order_by('-id'))
    #
    # # 查询条件
    # # 根据字符
    # # 忽略大小写
    # print(Classes.objects.get(name__iexact="PYTHON"))
    # # 包含 你指定的字符
    # print(Classes.objects.filter(name__contains='t'))
    # # 以什么字符开始
    # print(Classes.objects.filter(name__startswith="d"))
    # # 以什么字符结尾
    # print(Classes.objects.filter(name__endswith="k"))
    # # 成员 字段值是否在你给定范围里面
    # # print(Classes.objects.filter(name__in=["python","java"]))
    # # 根据数字
    # # 大于
    # print(Classes.objects.filter(id__gt=2))
    # # 大于等于
    # print(Classes.objects.filter(id__gte=4))
    # # 小于
    # print(Classes.objects.filter(id__lt=7))
    # # 小于等于
    # print(Classes.objects.filter(id__lte=6))
    # # 区间  数值范围  这个和range不一样左右都包括
    # print(Classes.objects.filter(id__range=(3,6)))
    # # 判断是否为空
    # print("*"*20)
    # print(Classes.objects.filter(name__isnull=True))
    return HttpResponse("数据更新成功")


def tab_info(request, **kwargs):
    # # 一对一表数据添加
    # # 创建一个用户对象 密码是明文保存
    # user = User(username="rose")
    # # # 密码进行加密的操作
    # user.set_password('qwe123')
    # user.save()  # 保存加密后的密码
    # # 创建一个用户详情对象
    # # 如果上面有用户对象则直接关联用户对象
    # UserDetail.objects.create(age=19, sex=0, user=user)
    # # 如果没有对应的关联用户对象，可以通过用户id指定关联的用户对象
    # UserDetail.objects.create(age=19, sex=0, user_id=3)
    #
    # # 一对一表数据查询
    # # 获取用户为lisa的年龄
    # # 先获取用户对象
    # user=User.objects.get(username='lisa')
    # # 因为用户表关联了用户详情表 所有用户对象拥有用户详情表属性 相当于得到的了用户详情对象
    # # 可以直接通过.字段名获取到对应的属性值
    # print(user.userdetail.age)
    #
    # # 获取id为1的用户详情的名字
    # # 先得到用户详情对象
    # userdetail=UserDetail.objects.get(id=1)
    # print(userdetail.user.username)

    # 一对多表数据添加
    # 创建一个分类表对象
    classes = Classes.objects.create(name='test')
    # 创建一个文章对象
    article = Article(title="test111", desc='test-111', content='test-content111',
                      cover='https://i-blog.csdnimg.cn/direct/693e9501a2ca4875ae4a1ef7b48b3380.png',
                      user_id=1, classes_id=8)
    article.save()

    # 一对多表数据查询
    # 得到test分类下的所有文章对象
    # 第一步得到test分类
    classes=Classes.objects.get(name='test')
    # 得到python分类下 文章名字为test的文章内容
    print(classes.article_set.get(title="test").content)

    # 一对多管理器表数据添加
    classes=Classes.objects.get(name='python')
    # 通过管理器来添加
    classes.article_set.create(title="test222", desc='test222', content='test222',
                               cover="https://i-blog.csdnimg.cn/direct/693e9501a2ca4875ae4a1ef7b48b3380.png",
                               user_id=1, classes=classes)

    # 一对多管理器表修改 添加
    # 从test分类下修改到python分类下
    # 在python分类下添加了一篇文章
    classes = Classes.objects.get(name='python')
    classes.article_set.add(Article.objects.get(title="test111"))

    # 多对多的表数据添加
    # 给test1这篇文章添加两个标签
    # 第一步先获取test1文章
    article = Article.objects.get(title="test111")

    article.label.add(Label.objects.get(name="test111"))
    article.label.create(name="test111")
    article.label.create(name="test222")

    # 表数据删除
    # 删除的是关系表中的记录 不会删除标签表的标签数据
    # 第一步先获取test1文章
    article = Article.objects.get(title="test111")
    # 第二步获取'test222'的标签
    article.label.remove(Label.objects.get(name='test222'))
    # 删除全部数据 关系表中的记录
    article.label.clear()

    # 多表查询
    # 获取文章标题为test111的标签
    # 第一步先获取test1文章
    article = Article.objects.get(title="test111")
    label=Label.objects.get(name="test111")
    print(article.label.all())
    print(label.article_set.all())

    # 获取id为1的分类的文章的用户详情
    print(UserDetail.objects.filter(user__article__classes_id=6))
    # 获取的是python分类下的所有文章
    print(Article.objects.filter(classes__name="python"))

    return HttpResponse('成功')


class AddArticleView(View):
    # 添加登录校验的权限 用的是auth系统 不会在session表中查询
    @method_decorator(login_required)
    def get(self, request):
        form = ArticleForms()
        # username = request.session.get("username")
        username = request.user.username
        print(username)
        return render(request, 'demo/add_article.html', {'user': username, 'form': form})

    def post(self, request):
        # 绑定用户数据表单对象
        # 当需要使用表单时并且需要绑定文件数据 文件数据需要跟普通的表单数据分开处理
        # 文件数据并不会放到POST 而是存放到file参数里面
        form = ArticleForms(request.POST, request.FILES)
        print(request.POST)
        print(request.FILES)
        username = request.user.username
        if form.is_valid():
            title = form.cleaned_data.get('title')
            desc = form.cleaned_data.get('desc')
            content = form.cleaned_data.get('content')
            cover = form.cleaned_data.get('cover')
            classes = form.cleaned_data.get('classes')
            # 多对多的表关系 得到多个数据 QuerySet对象
            labels = form.cleaned_data.get('label')
            user = request.user
            # 保存图片时  图片名可能会出现重复
            # 为了图片的名字不重复 重写图片名字 把时间戳跟图片名拼接
            file_name = f'{datetime.now().strftime("%Y%m%d%H%M%S")}_{cover.name}'
            # 图片地址
            with open(os.path.join(MEDIA_ROOT, file_name), 'wb') as fp:
                # 当你的图片文件过大的时候 chunks 上传的文件数据分批次写入
                for data in cover.chunks():
                    fp.write(data)

            #   创建文章模型对象
            article = Article.objects.create(title=title, desc=desc, content=content, cover=file_name, user=user,
                                             classes=classes)
            # 多对多对已有数据的绑定
            for i in labels:
                article.label.add(i)

        return render(request, 'demo/add_article.html', {'user': username, 'form': form})


class ArticleDetailView(View):
    # 查看一篇文章的详情内容 需要通过id知道查看的是那一篇文章
    def get(self, request, id):
        try:
            article = Article.objects.get(id=id)
        except Article.DoesNotExist:
            return redirect('not_find')

        return render(request, 'demo/article_detail.html', context={'article': article, 'labels': article.label.all()})

# 创作中心页面 跟文章添加页面一致需要先登录才能查看
# 文章分页
class ArticleUserView(View):
    # 添加权限校验
    @method_decorator(login_required)
    def get(self, request):
        # 查询到的该用户发布的所有文章信息
        articles = Article.objects.filter(user=request.user)
        # 自动把文章信息做分页的操作
        # 每页只显示两篇文章 创建一个分页器对象
        pg = Paginator(articles, 2)
        try:
            pg_current = request.GET.get('page')  # 获取当前的页面页码
            # 获取当前的页面数据（对象）
            pages = pg.page(pg_current)
        # 如果页面不是一个整数，默认显示第一页
        except PageNotAnInteger:
            pages = pg.page(1)
        #     当page参数超过有效范围就显示最后一页
        except EmptyPage:
            pages = pg.page(pg.num_pages)
        return render(request, 'demo/user_article.html',
                      {"user": request.user.username, 'articles': articles, 'pages': pages})


# 文章修改
class ArticleUpdateView(View):
    # 添加权限校验
    @method_decorator(login_required)
    # 你要修改哪篇文章
    def get(self, request, id):
        form = ArticleForms()
        try:
            article = Article.objects.get(id=id)
        except Article.DoesNotExist:
            return redirect('not_find')
        else:
            # 如果能够正常获取到文章对象数据，就定义一个字典保存修改
            data = {
                "title": article.title,
                "desc": article.desc,
                "content": article.content,
                "classes": article.classes,
                "label": article.label.all()
            }
        form = ArticleForms(data)
        return render(request, 'demo/update_article.html', {"user": request.user.username, "form": form})

    def post(self, request, id):
        # 绑定用户数据表单对象
        # 当需要使用表单时并且需要绑定文件数据 文件数据需要跟普通的表单数据分开处理
        # 文件数据并不会放到POST 而是存放到file参数里面
        form = ArticleForms(request.POST, request.FILES)
        print(request.POST)
        print(request.FILES)
        username = request.user.username
        if form.is_valid():
            title = form.cleaned_data.get('title')
            desc = form.cleaned_data.get('desc')
            content = form.cleaned_data.get('content')
            cover = form.cleaned_data.get('cover')
            classes = form.cleaned_data.get('classes')
            # 多对多的表关系 得到多个数据 QuerySet对象
            labels = form.cleaned_data.get('label')
            user = request.user
            # 保存图片时  图片名可能会出现重复
            # 为了图片的名字不重复 重写图片名字 把时间戳跟图片名拼接
            file_name = f'{datetime.now().strftime("%Y%m%d%H%M%S")}_{cover.name}'
            # 图片地址
            with open(os.path.join(MEDIA_ROOT, file_name), 'wb') as fp:
                # 当你的图片文件过大的时候 chunks 上传的文件数据分批次写入
                for data in cover.chunks():
                    fp.write(data)

            # 查询获取之前的文章信总
            articles = Article.objects.filter(id=id)
            # 除之前保存的文章图片
            os.remove(os.path.join(MEDIA_ROOT, articles[0].cover))
            # 保存到数据库中

            #   创建文章模型对象
            articles.update(title=title, desc=desc, content=content, cover=file_name, user=user,
                            classes=classes)

            articles[0].label.clear()
            # 多对多对已有数据的绑定
            for i in labels:
                articles[0].label.add(i)
        return redirect('ArticleUser')







# 文章删除
class ArticleDelete(View):
    def get(self, request, id):
        try:
            article = Article.objects.get(id=id)
        except Article.DoesNotExist:
            return redirect('not_find')
        # 删除文章封面图
        os.remove(os.path.join(MEDIA_ROOT, article.cover))
        # 删除数据库里面的文章数据
        article.delete()
        return redirect('ArticleUser')
